<?php
/**
 * CURL数据传输
 * @author wsfuyibing <websearch@163.com>
 * @source [PATH]project/module/curl.php
 * @date 2014-07-10
 */
class CurlModule{
	/**
	 * 属性描述
	 * @property boolean $_ENCODE, 自动对参数URLENCODE;
	 * @property boolean $_USESSL, 使用SSL协议
	 * @property string $_METHOD, 请求方式(DELETE|GET|POST|PUT)
	 * @property int $_TIMEOUT, 超时时长(秒)
	 */
	private $_ENCODE = true;
	private $_HEADERS = array('http' => 0.0, 'code' => 100, 'header' => array(), 'elements' => array(), 'location' => '', 'method' => '');
	private $_USESSL = false;
	private $_METHOD = 'GET';
	private $_TIMEOUT = 10;

	/**
	 * 用户参数
	 * @property int $_UCOOKIE, $_COOKIE
	 * @property int $_UHEADER, $_SERVER
	 * @property int $_UGET, $_GET
	 * @property int $_UPOST, $_POST
	 */
	private $_UCOOKIE = array();
	private $_UHEADER = array();
	private $_UGET = array();
	private $_UPOST = array();
	/**
	 * 构造方法
	 */
	public function __construct(){
		/**
		 * 词典编码
		 */
		function_exists('curl_init') || E('PHP_EXTENSION_DISABLED', array('module' => 'CURL'));
		/**
		 * 超时配置
		 */
		$_TIMEOUT = (int) S('CURL_TIMEOUT');
		$_TIMEOUT > 0 && $this->_TIMEOUT = $_TIMEOUT;
		$_ENCODE = S('CURL_ENCODE'); 
		is_bool($_ENCODE) && $this->_ENCODE = $_ENCODE;
	}
	/**
	 * 传输COOKIE
	 * @param string, 当前浏览器记录的全部COOKIE
	 * @param array, 指定名称和值
	 * @example M('curl')->C('*')->get(URL), 浏览下记录的全部COOKIE
	 * @example M('curl')->C('name1,name2')->get(URL)
	 * @example M('curl')->C(array('key' => 'val'))->get(URL)
	 */
	public function C($p){
		isset($_COOKIE) && is_array($_COOKIE) || $_COOKIE = array(); if (is_array($p)){
			count($p) && 
			$this->_UCOOKIE = $p;
		} else if ($p === '*'){
			$this->_UCOOKIE = $_COOKIE;
		} else if (is_string($p)){
			foreach (explode(',', $p) as $k){
				isset($_COOKIE[$k]) && $this->_UCOOKIE[$k] = $_COOKIE[$k];
			}
		}
		/**
		 * 递进返回
		 */
		return $this;
	}
	/**
	 * 传输GET
	 * @param array, 指定内容
	 * @example M('curl')->G('*')->get(URL)
	 * @example M('curl')->G('key=val')->get(URL)
	 * @example M('curl')->G(array('key' => 'val'))->get(URL)
	 */
	public function G($p){
		if (is_array($p)){
			count($p) && $this->_UGET = $p;
		} else if ($p === '*'){
			isset($_GET) && is_array($_GET) && count($_GET) && 
			$this->_UGET = $p;
		} else if (is_string($p)){
			foreach (explode('&', $p) as $s){
				$s = trim($s); if ($s != ''){
					$ss = explode('=', $s);
					$si = count($ss);
					$this->_UGET[$ss[0]] = $si > 1 ? $ss[1] : '';
				}
			}
		}
		/**
		 * 递进返回
		 */
		return $this;
	}
	/**
	 * 传输HEADER
	 * @param array, 指定内容
	 * @example M('curl')->H(array('content-type: text/html'))->get(URL)
	 * @example M('curl')->H(array('content-type' => 'text/html'))->get(URL)
	 */
	public function H($p){
		is_array($p) && count($p) && 
		$this->_UHEADER = $p;
		/**
		 * 递进返回
		 */
		return $this;
	}
	/**
	 * 传输POST
	 * @param array, 指定内容
	 * @example M('curl')->P('*')->post(URL)
	 * @example M('curl')->G('key=val')->post(URL)
	 * @example M('curl')->P(array('key' => 'val'))->post(URL)
	 */
	public function P($p){
		if (is_array($p)){
			count($p) && 
			$this->_UPOST = $p;
		} else if ($p === '*'){
			isset($_POST) && is_array($_POST) && count($_POST) && 
			$this->_UPOST = $_POST;
		} else if (is_string($p)){
			foreach (explode('&', $p) as $s){
				$s = trim($s); if ($s != ''){
					$ss = explode('=', $s);
					$si = count($ss);
					$this->_UPOST[$ss[0]] = $si > 1 ? $ss[1] : '';
				}
			}
		}
		/**
		 * 递进返回
		 */
		return $this;
	}
	/**
	 * URLENCODE处理
	 */
	public function encode($p = true){
		is_bool($p) && $this->_ENCODE = $p;
		/**
		 * 递进返回
		 */
		return $this;
	}
	/**
	 * 完成后同时执行哪些Header参数
	 * @param string|array, $p 
	 * @example M('curl')->header('*')->get(URL) 全部参数
	 * @example M('curl')->header('set-cookie,location')->get(URL) 指定Cookie, Location; 以逗号分隔
	 * @example M('curl')->header(array('set-cookie','location'))->get(URL) 按数组设置
	 */
	public function header($p){
		if ($p === '*'){
			$this->_HEADERS['header'] = '*';
		} else if (is_array($p) && count($p)){
			foreach ($p as $k => $v){
				$this->_HEADERS['header'] = strtolower($v);
			}
		} else if (is_string($p)){
			foreach (explode(',', $p) as $s){
				$this->_HEADERS['header'][] = strtolower($s);
			}
		}
		/**
		 * 递进返回
		 */
		return $this;
	}
	/**
	 * CURL请求
	 */
	public function get($p, $return = false){
		$this->_METHOD = 'GET';
		return $this->_connect($p, $return);
	}
	public function post($p, $return = false){
		$this->_METHOD = 'POST';
		return $this->_connect($p, $return);
	}
	/**
	 * 公共方法
	 */
	private function _connect(& $p, & $return){
		$this->_HEADERS['method'] = & $this->_METHOD;
		/**
		 * 格式化URL并转数组
		 */
		$p = preg_replace("/\&amp;/i", '&', $p);
		$a = parse_url($p);
		/**
		 * 协议HTTP/HTTPS
		 */
		$a['scheme'] = wsc::isarr($a, 'scheme');
		$a['scheme'] = $a['scheme'] ? strtolower($a['scheme']) : 'http';
		$a['scheme'] === 'https' && $this->_USESSL = true;
		/**
		 * 端口
		 */
		$a['host'] = wsc::isarr($a, 'host');
		$a['host'] = $a['host'] ? strtolower($a['host']) : '127.0.0.1';
		/**
		 * 端口
		 */
		$a['port'] = wsc::isarr($a, 'port');
		$a['port'] && $a['port'] = ':'.$a['port'];
		/**
		 * 路径
		 */
		$a['path'] = wsc::isarr($a, 'path');
		$a['path'] = $a['path'] ? str_replace('\\', '/', $a['path']) : '/';
		/**
		 * 参数
		 */
		$a['query'] = wsc::isarr($a, 'query'); if ($a['query']){
			foreach (explode('&', $a['query']) as $_str){
				$_str = trim($_str); if ($_str != ''){
					$_strs = explode('=', $_str); $_stri = count($_strs);
					$_strs[0] = urldecode($_strs[0]);
					$_strs[1] = $_stri == 2 ? urldecode($_strs[1]) : '';
					$this->_UGET[$_strs[0]] = $_strs[1];
				}
			}
		}
		/**
		 * 参数
		 */
		$a['fragment'] = wsc::isarr($a, 'fragment');
		$a['fragment'] && $a['fragment'] = '#'.$a['fragment'];
		/**
		 * 重组
		 */
		$p = $a['scheme'].'://'.$a['host'].$a['port'].$a['path'];
		$comma = '?';
		/**
		 * 附加GET
		 */
		foreach ($this->_UGET as $_gk => $_gv){
			$p .= $comma; $comma = '&'; $this->_ENCODE && $_gk = urlencode($_gk);
			$p .= $_gk; if ($_gv !== ''){
				$this->_ENCODE && $_gv = urlencode($_gv);
				$p .= '='.$_gv;
			}
		} $p .= $a['fragment'];
		/**
		 * 建立资源
		 */
		$this->_HEADERS['location'] = & $p; if (!$query = curl_init()){
			return $this->_error('CURL_CONNECT', array('host' => $a['host']), $return);
		}
		/**
		 * 基本次数
		 * @var string $url, 完整网址
		 * @const CURLOPT_URL, 指定URL地址
		 * @const CURLOPT_FOLLOWLOCATION, 禁用Location递归
		 * @const CURLOPT_RETURNTRANSFER, 以文件流返回而非输出
		 * @const CURLOPT_HEADER, 屏蔽头文件内容
		 * @const CURLOPT_TIMEOUT, 设置超时时长(秒)
		 */
		curl_setopt($query, CURLOPT_URL, $p);
		curl_setopt($query, CURLOPT_FOLLOWLOCATION, 0);
		curl_setopt($query, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($query, CURLOPT_TIMEOUT, $this->_TIMEOUT);
		curl_setopt($query, CURLOPT_HEADER, 1);
		/**
		 * SSL安全加密
		 * @const CURLOPT_SSL_VERIFYHOST, [验证]检查主机名
		 * @const CURLOPT_SSL_VERIFYPEER, [验证]禁用后cURL将终止从服务端进行验证
		 */
		if ($this->_USESSL){
			curl_setopt($query, CURLOPT_SSL_VERIFYHOST, false);
			curl_setopt($query, CURLOPT_SSL_VERIFYPEER, 0);
		}
		/**
		 * 请求类型
		 */
		curl_setopt($query, CURLOPT_CUSTOMREQUEST, $this->_METHOD);
		/**
		* POST请求
		*/
		if ($this->_METHOD === 'POST'){
			curl_setopt($query, CURLOPT_POSTFIELDS, $this->_UPOST);
		}
		/**
		* HEADER
		*/
		if (count($this->_UHEADER)){
			$header = array(); foreach ($this->_UHEADER as $k => $v){
				$v = str_replace(' ', '', $v); if (is_numeric($k)){
					$header[] = $v;
				} else {
					$header[] = $k.':'.$v;
				}
			} curl_setopt($query, CURLOPT_HTTPHEADER, $header);
		}
		/**
		* COOKIE
		*/
		if (count($this->_UCOOKIE)){
			$cookie = $comma = ''; foreach ($this->_UCOOKIE as $k => $v){
				$cookie .= $comma; $comma = ';';
				$cookie .= $k.'='.$v;
			} curl_setopt($query, CURLOPT_COOKIE, $cookie);
		}
		/**
		* 执行并返回
		*/
		$buffer = curl_exec($query);
		curl_close($query);
		/**
		 * 读取Header
		 */
		$this->_getheader($buffer);
		$this->_setheader();
		/**
		 * 成功请求
		 */
		$_codes = S('CURL_CODE_SUCCESS'); is_array($_codes) && count($_codes) || 
		$_codes = array(200);
		if (in_array($this->_HEADERS['code'], $_codes)){
			if ($return){
				return $buffer;
			} echo $buffer; exit;
		}
		/**
		 * 请求错误
		 */
		return $this->_error('CURL_ERROR_'.$this->_HEADERS['code'], $this->_HEADERS, $return);
	}
	private function _error(){
		$_ln = 'CURL_ERROR'; $_return = false; $_ai = func_num_args(); if ($_ai){
			$_as = func_get_args();
			$_ln = $_as[0];
			$_return = $_as[$_ai-1];
		} $_msg = L($_ln);
		/**
		 * 返回或输出
		 */
		if ($_return){
			return $_msg;
		} echo $_msg; exit;
	}
	private function _getheader(& $buffer){		
		/**
		* 不同服务器可能返回如下代码前置
		* @example HTTP/1.1 100 Continue
		*/
		if (preg_match_all("/^HTTP\/(\d+)\.(\d+)(\s+)(\d+)(.+?)\r\n/i", $buffer, $https)){
			/**
			 * 文件头检查
			 */
			foreach ($https[4] as $i => $code){
				$code = (int) $code; if ($code != 100){
					$this->_HEADERS['code'] = $code;
					$this->_HEADERS['http'] = $https[1][$i].'.'.$https[2][$i];
					break;
				}
			}
			/**
			 * 文件头解析
			 */
			$buffer = preg_replace("/^HTTP\/(\d+)\.(\d+)(\s+)(\d+)(.+?)\r\n\r\n/i", '', $buffer);
			/**
			 * 文件头提取
			 */
			$point = strpos($buffer, "\r\n\r\n") + 4;
			$header = substr($buffer, 0, $point); $this->_HEADERS['headers'] = & $header;
			$buffer = substr($buffer, $point);
			/**
			 * 头元素解析
			 */
			if ($header && preg_match_all("/([_a-z0-9\-]+):(.+?)\r\n/is", $header, $heads)){
				$this->_HEADERS['elements'] = array_combine($heads[1], $heads[2]);
			}
		}
	}
	private function _setheader(){
		foreach ($this->_HEADERS['elements'] as $k => $v){
			$q = $this->_HEADERS['header'] === '*' ? true : false; if (!$q && is_array($this->_HEADERS['header'])){
				$s = strtolower($k); if (in_array($s, $this->_HEADERS['header']) && $v != ''){
					$q = true;
				}
			} if ($q){
				@header($k.': '.$v);
			}
		}
	}
}